﻿/*
 * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
 *
 * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
 *
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
 */

#ifndef UTIL_RECYCLEPOOL_H_
#define UTIL_RECYCLEPOOL_H_

#include "List.h"
#include <atomic>
#include <deque>
#include <functional>
#include <memory>
#include <mutex>
#include <unordered_set>

namespace toolkit
{

#if (defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ >= 4 && __GNUC_MINOR__ >= 9))) || defined(__clang__)             \
    || !defined(__GNUC__)
#define SUPPORT_DYNAMIC_TEMPLATE
#endif

	template <typename C>
	class ResourcePool_l;
	template <typename C>
	class ResourcePool;

	template <typename C>
	class shared_ptr_imp : public std::shared_ptr<C>
	{
	public:
		shared_ptr_imp() {}

		/**
		 * 构造智能指针
		 * @param ptr 裸指针
		 * @param weakPool 管理本指针的循环池
		 * @param quit 对接是否放弃循环使用
		 */
		shared_ptr_imp(
			C* ptr, const std::weak_ptr<ResourcePool_l<C>>& weakPool, std::shared_ptr<std::atomic_bool> quit,
			const std::function<void(C*)>& on_recycle);

		/**
		 * 放弃或恢复回到循环池继续使用
		 * @param flag
		 */
		void quit(bool flag = true)
		{
			if (_quit)
			{
				*_quit = flag;
			}
		}

	private:
		std::shared_ptr<std::atomic_bool> _quit;
	};

	template <typename C>
	class ResourcePool_l : public std::enable_shared_from_this<ResourcePool_l<C>>
	{
	public:
		using ValuePtr = shared_ptr_imp<C>;
		friend class shared_ptr_imp<C>;
		friend class ResourcePool<C>;

		ResourcePool_l()
		{
			_alloc = []() -> C* { return new C(); };
		}

#if defined(SUPPORT_DYNAMIC_TEMPLATE)
		template <typename... ArgTypes>
		ResourcePool_l(ArgTypes &&...args)
		{
			_alloc = [args...]()->C* { return new C(args...); };
		}
#endif // defined(SUPPORT_DYNAMIC_TEMPLATE)

		~ResourcePool_l()
		{
			for (auto ptr : _objs)
			{
				delete ptr;
			}
		}

		void setSize(size_t size)
		{
			_pool_size = size;
			_objs.reserve(size);
		}

		ValuePtr obtain(const std::function<void(C*)>& on_recycle = nullptr)
		{
			return ValuePtr(getPtr(), _weak_self, std::make_shared<std::atomic_bool>(false), on_recycle);
		}

		std::shared_ptr<C> obtain2()
		{
			auto weak_self = _weak_self;
			return std::shared_ptr<C>(getPtr(), [weak_self](C* ptr)
				{
					auto strongPool = weak_self.lock();
					if (strongPool)
					{
						//放入循环池
						strongPool->recycle(ptr);
					}
					else
					{
						delete ptr;
					}
				});
		}

	private:
		void recycle(C* obj)
		{
			auto is_busy = _busy.test_and_set();
			if (!is_busy)
			{
				//获取到锁
				if (_objs.size() >= _pool_size)
				{
					delete obj;
				}
				else
				{
					_objs.emplace_back(obj);
				}
				_busy.clear();
			}
			else
			{
				//未获取到锁
				delete obj;
			}
		}

		C* getPtr()
		{
			C* ptr;
			auto is_busy = _busy.test_and_set();
			if (!is_busy)
			{
				//获取到锁
				if (_objs.size() == 0)
				{
					ptr = _alloc();
				}
				else
				{
					ptr = _objs.back();
					_objs.pop_back();
				}
				_busy.clear();
			}
			else
			{
				//未获取到锁
				ptr = _alloc();
			}
			return ptr;
		}

		void setup() { _weak_self = this->shared_from_this(); }

	private:
		size_t _pool_size = 8;
		std::vector<C*> _objs;
		std::function<C* (void)> _alloc;
		std::atomic_flag _busy{ false };
		std::weak_ptr<ResourcePool_l> _weak_self;
	};

	/**
	 * 循环池，注意，循环池里面的对象不能继承enable_shared_from_this！
	 * @tparam C
	 */
	template <typename C>
	class ResourcePool
	{
	public:
		using ValuePtr = shared_ptr_imp<C>;
		ResourcePool()
		{
			pool.reset(new ResourcePool_l<C>());
			pool->setup();
		}
#if defined(SUPPORT_DYNAMIC_TEMPLATE)
		template <typename... ArgTypes>
		ResourcePool(ArgTypes &&...args)
		{
			pool = std::make_shared<ResourcePool_l<C>>(std::forward<ArgTypes>(args)...);
			pool->setup();
		}
#endif // defined(SUPPORT_DYNAMIC_TEMPLATE)
		void setSize(size_t size) { pool->setSize(size); }

		//获取一个对象，性能差些，但是功能丰富些
		ValuePtr obtain(const std::function<void(C*)>& on_recycle = nullptr) { return pool->obtain(on_recycle); }

		//获取一个对象，性能好些
		std::shared_ptr<C> obtain2() { return pool->obtain2(); }

	private:
		std::shared_ptr<ResourcePool_l<C>> pool;
	};

	template<typename C>
	shared_ptr_imp<C>::shared_ptr_imp(C* ptr,
		const std::weak_ptr<ResourcePool_l<C> >& weakPool,
		std::shared_ptr<std::atomic_bool> quit,
		const std::function<void(C*)>& on_recycle) :
		std::shared_ptr<C>(ptr, [weakPool, quit, on_recycle](C* ptr)
			{
				if (on_recycle)
				{
					on_recycle(ptr);
				}
				auto strongPool = weakPool.lock();
				if (strongPool && !(*quit))
				{
					//循环池还在并且不放弃放入循环池
					strongPool->recycle(ptr);
				}
				else
				{
					delete ptr;
				}
			}), _quit(std::move(quit))
	{
	}

} /* namespace toolkit */
#endif /* UTIL_RECYCLEPOOL_H_ */
